package gui;

import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Set;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;

import semantica.AnaliseSemantica;
import semantica.SymbolTable;

import intermedia.DirectedEdge;
import intermedia.Edge;
import intermedia.Graph;
import intermedia.InvalidTypeAttributeException;
import intermedia.Node;
import intermedia.ProduceCode;
import intermedia.UndirectedEdge;

import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.view.mxGraph;

public class JgraphGUI extends JFrame {

	private mxGraph graph;
	private mxGraphComponent graphComponent;
	private Graph myGraph;
	private Hashtable<Node, Object> vertexes;
	private ArrayList<Object> all_cells;

	public JgraphGUI(Graph g) {
		myGraph = g;
		graph = new mxGraph();
		vertexes = new Hashtable<Node, Object>();
		all_cells = new ArrayList<Object>();
		initGUI();
		desenhaGraph();

		graphComponent = new mxGraphComponent(graph);
		graphComponent.setPreferredSize(new Dimension(800, 600));
		graphComponent.setConnectable(false);
		graphComponent.getGraphControl()
				.addMouseListener(new MyMouseListener());
		getContentPane().add(graphComponent);

		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setSize(800, 600);
		setVisible(true);
	}

	private void initGUI() {
		setLayout(new FlowLayout(FlowLayout.LEFT));

		JButton produz_codigo = new JButton("Generate Code");
		getContentPane().add(produz_codigo);
		produz_codigo.addActionListener(new ProduzCodigoActionListener());

		JButton adiciona_no = new JButton("New Node");
		getContentPane().add(adiciona_no);
		adiciona_no.addActionListener(new AdicionaNoListener());

		JButton adiciona_aresta = new JButton("New Edge");
		getContentPane().add(adiciona_aresta);
		adiciona_aresta.addActionListener(new AdicionaArestaListener());

		JButton adiciona_atributo = new JButton("New Attribute");
		getContentPane().add(adiciona_atributo);
		adiciona_atributo.addActionListener(new AdicionaAtributoListener());
	}

	private void desenhaGraph() {

		for (Object cell : all_cells)
			graph.getModel().remove(cell);
		all_cells.clear();

		graph.getModel().beginUpdate();
		try {

			for (Node node : myGraph.getNodes()) {
				if (!node.visited) {
					desenhaNode(node);
				}
			}

			for (Node node : myGraph.getNodes()) {
				desenhaArestas(node);
			}

		} finally {
			graph.getModel().endUpdate();
		}

		for (Node node : myGraph.getNodes()) {
			node.visited = false;
			for (Edge e : node.getEdges())
				e.visited = false;
		}

	}

	private void desenhaArestas(Node node) {
		Object parent = graph.getDefaultParent();

		for (Edge e : node.getEdges()) {
			if (!e.visited) {
				e.visited = true;
				Object src = vertexes.get(e.getSrc());
				Object dst = vertexes.get(e.getDst());

				String color;
				if (e.getAttributes().attributes.get("color") != null)
					color = "strokeColor="
							+ e.getAttributes().attributes.get("color");
				else
					color = "";

				Object e1 = graph.insertEdge(parent, null, "", src, dst, color);
				all_cells.add(e1);

				if (e instanceof UndirectedEdge) {
					Object e2 = graph.insertEdge(parent, null, "", dst, src,
							color);
					all_cells.add(e2);
				}
			}
		}

	}

	private void desenhaNode(Node node) {
		node.visited = true;
		Object parent = graph.getDefaultParent();

		String color;
		if (node.getAttributes().attributes.get("color") != null)
			color = "fillColor=" + node.getAttributes().attributes.get("color");
		else
			color = "";

		Object v1 = graph.insertVertex(parent, null, node.getId(), node.getX(),
				node.getY(), 80, 30, color);
		vertexes.put(node, v1);
		all_cells.add(v1);
	}

	private class ProduzCodigoActionListener implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent arg0) {

			String path;

			if (AnaliseSemantica.path == null) {
				path = JOptionPane
						.showInputDialog("What's the path of the file?");
			}
			else{
				path= AnaliseSemantica.path;
				System.out.println("Saving file " + path);
			}
			
			if (path == null)
				return;

			try {
				new ProduceCode(myGraph, path);
			} catch (IOException e) {
				System.out.println(e.getMessage());
			}
		}

	}

	private class AdicionaNoListener implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			String node_id = JOptionPane.showInputDialog("What's the node id?");
			if (node_id == null)
				return;

			if (myGraph.nodeExists(node_id)) {
				System.out.println("Node already exists!");
				return;
			}

			Node novo = new Node(node_id, new SymbolTable());

			myGraph.addNode(novo);

			desenhaGraph();
		}

	}

	private class AdicionaArestaListener implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent arg0) {

			String edgeType = JOptionPane
					.showInputDialog("Edge type (directed/undirected)?");

			if (edgeType == null
					|| !(edgeType.toLowerCase().equals("directed") || edgeType
							.toLowerCase().equals("undirected"))) {
				return;
			}

			if (edgeType.toLowerCase().equals("directed")
					&& !myGraph.isDigraph())
				System.out.println("Warning! Directed edge!");

			if (edgeType.toLowerCase().equals("undirected")
					&& myGraph.isDigraph()) {
				System.out
						.println("Semantic Error! Undirected Edge in a Directed Graph!");
				return;
			}

			String from_id = JOptionPane
					.showInputDialog("What's the initial node?");
			Node from = null;
			for (Node node : myGraph.getNodes()) {
				if (node.getId().equals(from_id)) {
					from = node;
					break;
				}
			}

			if (from == null)
				return;

			String to_id = JOptionPane.showInputDialog("What's the end node?");
			Node to = null;
			for (Node node : myGraph.getNodes()) {
				if (node.getId().equals(to_id)) {
					to = node;
					break;
				}
			}

			if (to == null)
				return;

			Edge e;

			if (edgeType.toLowerCase().equals("directed")) {
				e = new DirectedEdge(from, to, new SymbolTable());
			} else {
				e = new UndirectedEdge(from, to, new SymbolTable());
			}

			from.addEdge(e);
			to.addEdge(e);

			desenhaGraph();
		}
	}

	private class AdicionaAtributoListener implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent arg0) {
			String nodeId = JOptionPane.showInputDialog("What's the node id?");

			if (nodeId == null)
				return;

			ArrayList<Node> nodes = myGraph.getNodes();

			Node node = null;

			for (Node n : nodes) {
				if (n.getId().equals(nodeId)) {
					node = n;
				}
			}

			if (node == null) {
				System.out.println("Node with id " + nodeId + " not found!");
				return;
			}

			String key = JOptionPane
					.showInputDialog("What's the attribute's key?");

			if (key == null)
				return;

			String val = JOptionPane
					.showInputDialog("What's the attribute's value?");
			if (val == null)
				return;

			try {
				node.getAttributes().addAtribute(key, val);
				desenhaGraph();
			} catch (InvalidTypeAttributeException e) {
				System.out.println(e.getMessage());
			}

		}

	}

	private class MyMouseListener extends MouseAdapter {

		Object selected;

		public MyMouseListener() {
			selected = null;
		}

		@Override
		public void mouseClicked(MouseEvent e) {
		}

		@Override
		public void mouseEntered(MouseEvent e) {
			Set<Node> nodes = vertexes.keySet();

			for (Node node : nodes) {
				Object n = vertexes.get(node);

				if (n == selected) {
					node.setX(e.getX());
					node.setY(e.getY());
				}
			}

			selected = null;
		}

		@Override
		public void mouseExited(MouseEvent e) {

		}

		@Override
		public void mousePressed(MouseEvent e) {
			selected = graphComponent.getCellAt(e.getX(), e.getY());
		}

		@Override
		public void mouseReleased(MouseEvent e) {

		}

	}
}